#include"client.h"
#include<proto.h>
#include<iostream>
#include<stdlib.h>
#include<sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<getopt.h>
#include<unistd.h>
#include<fcntl.h>
#include<cstring>
#include<net/if.h>
#include<arpa/inet.h>
using namespace std;



client_cfg_st client_cfg = {
    .recvport = RECVPORT,
    .mgroup = MULTIGROUP,
    .player = PLAYER
};
void print_help(){
    cout<<"-P  the port number"<<endl;
    cout<<"-M  the multi group number"<<endl;
    cout<<"-p  the player addr"<<endl;
    return;
}


ssize_t writen(int fd,const void *buf,size_t count){
    int pos=0;
    int len=0;
    while(count>0){ // 当想要写的字符个数没有写完时，就一直写
        len = write(fd,(char*)buf+pos,count);
        if(len<0){
            perror("write()");
            exit(1);
        }
        count-=len;
        pos+=len;
    }
    return pos;

}


int main(int argc, char* argv[]){

    int index=0;
    int c;
    option argarr[] = {
        {"port",1,NULL,'P'},
        {"mgroup",1,NULL,'M'},
        {"player",1,NULL,'p'},
        {"help",0,NULL,'H'},
        {NULL,0,NULL,0}
    };

    // while(1){
    //     c = getopt_long(argc,argv,"P:M:p:H",argarr,&index);     
    //     if(c<0){

    //     }else{
    //         switch(c){
    //             case 'P':
    //             client_cfg.recvport=optarg;
    //                 break;
    //             case 'M':
    //             client_cfg.mgroup = optarg;
    //                 break;
    //             case 'p':
    //             client_cfg.player = optarg;
    //                 break;
    //             case 'H':
    //                 print_help();
    //                 exit(0); 
    //                 // 一般打印完帮助信息之后就退出了，比如ls --help
    //                 break;
    //             default:
    //             cout<<"arg error"<<endl;
    //             abort();
    //                 break;
    //         }
    //     }

    // }
    
    int socket_fd = socket(AF_INET,SOCK_DGRAM,0);
    if(socket_fd<0){
        perror("socket()");
        exit(1);
    }
    ip_mreqn optval;
    if(inet_pton(AF_INET,client_cfg.mgroup,&optval.imr_multiaddr)<0){
        perror("inet_pton()");
        exit(1);
    }
//将多播组地址转成网络字节序放进结构体中
    inet_pton(AF_INET,"0.0.0.0",&optval.imr_address);
    //将0.0.0.0 即任何地址 放进结构体中
    optval.imr_ifindex = if_nametoindex("eth0"); 
    //当前使用的网络设备索引号，可以使用if_nametoindex将字符串转成对应索引号
    // 第一次设置setsockopt 加入多播组
    if(setsockopt(socket_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&optval,sizeof(optval))<0){
        perror("setsocket IP_ADD_MEMBERSHIP()");
        exit(0);
    }
    int optval2 = 1;
// 第二次设置setsockopt 加
    // if(setsockopt(socket_fd,IPPROTO_IP,IP_MULTICAST_LOOP,&optval2,sizeof(optval2))<0){
    //     perror("setsocketopt IP_MULTICAST_LOOP ()");
    //     exit(0);
    // }

    sockaddr_in client_addr,server_addr,romote_addr;
    socklen_t addrlen = sizeof(server_addr),romote_len = sizeof(romote_addr);
    memset(&client_addr,0,sizeof(client_addr));
    client_addr.sin_family=AF_INET;
    client_addr.sin_port = htons(atoi(client_cfg.recvport));
    inet_pton(AF_INET,"0.0.0.0",&client_addr.sin_addr.s_addr);
    //因为是udp，所以需要绑定本地ip地址
    if(bind(socket_fd,(sockaddr *)&client_addr,sizeof(client_addr))<0){
        perror("bind()");
        exit(0);
    }

    int pipe_fd[2]; //创建匿名管道的文件描述符

    if(pipe(pipe_fd)<0){
        perror("pipe()");
        exit(1);
    }

    pid_t pid = fork();
    if(pid<0){
        perror("fork()");
        exit(1);
    }
    if(pid==0){
        //子进程调用解码器
        //将不需要的文件描述符关闭，比如从父进程中复制来的socket，以及管道中不需要用的端
        close(socket_fd);
        close(pipe_fd[1]); // 关闭写端，0读1写

        //由于解码器 mpg123需要接收标准输入的内容，所以需要重定向，使用文件描述符0进行操作
        // 之所以使用mpg123 接收标准输入是因为，mpg123可以接收文件，url或者标准输入的内容
        // 标准输入内容需要mpg123命令后面加 -  ，而此时不可能使用文件或者url，所以只能以标准输入
        //进行读取，所以要重定向
        dup2(pipe_fd[0],0);
        //重定向之后，可以考虑将原始的pipe_fd[0] 关闭，因为重定向之后都使用标准输入了
        if(pipe_fd[0]>0){
            close(pipe_fd[0]); // 判断是否不为标准输入，如果不是标准输入则关闭
        }
        execl("/bin/sh","sh","-c",client_cfg.player,NULL); 
        //可以直接调用bash shell ，将之前的mpg123命令传递过去，相当于调用shell执行mpg123 命令
        // 而mpg123命令中又指定了从标准输入读取数据，而标准输入的数据上一步以及处理好了
        //，所以直接一步到位

        //由于execl执行完毕之后，整个进程内容都被替换了，所以下面一步正常情况下执行不到，如果
        // 执行到就是出错了
        perror("execl()");
        exit(1);
    }
    else{
        //父进程 从网络上接收数据，并将数据发送给子进程
        // 1，收节目单
        // 2，选择频道
        // 3，发送给子进程
        
        //首先根据一个频道list的最大长度将数据接收进来
        msg_list_st *channel_list =(msg_list_st*)malloc(MSG_LIST_MAX);//new msg_list_st[MSG_LIST_MAX];
        if(channel_list==NULL){
            perror("new msg_list_st");
            exit(1);
        }
        //接着就使用recvfrom 接收数据
        int recv_len = 0;
        while(1){
            if((recv_len = recvfrom(socket_fd,channel_list,MSG_LIST_MAX,0,(sockaddr*)&server_addr,&addrlen))<0){
                perror("recvfrom()");
                exit(0);
            }
            if(recv_len<sizeof(msg_list_st)){ 
                // 如果接收到的长度比结构体类型还要小，则说明出错了
            // 结构体类型的定义中数据部分的长度是为1，所以至少比这个长
            cout<<"recv data failed,len < min len"<<endl;
                // exit(0);
                continue;
            }else{
                //如果接收到是数据正常，分析是否是channel list的数据，如果是则继续分析
                if(channel_list->channid!=LISTCHANN){
                    cout<<"not LISTCHANN"<<endl;
                    continue;
                }
                break;
                //感觉有问题，把数据读了，则缓存中还有数据吗
                //可能不在意缓存问题，读掉了还有新的包传过来
            }

        }

        msg_list_entity_st *p=channel_list->entity;
        for( ; (msg_list_entity_st *)((char*)p+ntohs(p->data_len)) <= (msg_list_entity_st *)((char*)(channel_list->entity) + recv_len) && ntohs(p->data_len)>0; p = (msg_list_entity_st*)((char*) p + ntohs(p->data_len))){
            //这里的长度表示整个channel list的数据的长度，然后p从list结构体的entity开始向后移动
            //enity的结尾也就是channel list的结尾
            //p每次需要往后移动一个位置，注意是以字节为单位进行移动，不再是以一个entity为单位移动
            //所以向后移动时需要将p的类型强转成char* 这样才能每次移动一个char的长度
            cout<<"channel id [ "<<(int)(p->channid)<<" ]"<<(char*)p->desc<<endl;

        }

        int user_choose_channel_id=0;
        cout<<"choose the channel you like:";
        // while(cin>>user_choose_channel_id);
        while(1){
            int res = scanf("%d",&user_choose_channel_id);
            if(res!=1){
                cout<<"input number error"<<endl;
                exit(0);
            }
            if(user_choose_channel_id<0){
                cout<<"input number out of range"<<endl;
                continue;
            }else{
                break;
            }
        }
        cout<<"the number you choose is "<<user_choose_channel_id<<endl;
        // free(p);
        // free(channel_list);
        msg_channel_st *channel_st = new msg_channel_st[MAX_CHANNEL_MSG];
        int remote_recv_len = 0;
        cout<<"try get msg from choosed channed id "<<endl;
        while(1){
            remote_recv_len = recvfrom(socket_fd,channel_st,MAX_CHANNEL_MSG,0,(sockaddr*)&romote_addr,&romote_len);
            if(romote_addr.sin_port!=server_addr.sin_port || romote_addr.sin_addr.s_addr!=server_addr.sin_addr.s_addr){
                //如果这次收到的ip和端口和上次收到的list的ip和端口不一致
                cout<<"address is mot match"<<endl;
                continue;
            }
            if(remote_recv_len<sizeof(msg_channel_st)){ 
                // 如果读到的数据小于最小的大小，即结构体本身的大小
                cout<<"len is too short"<<endl;
                continue;
            }


            if(channel_st->channid==user_choose_channel_id){
                
                writen(pipe_fd[1],channel_st->data,(remote_recv_len-sizeof(channid_t))); 
                // 坚持写够多少字节 ，1为写入端
                //注意这里的remote_recv_len 就已经是msg_channel_st的实际长度了
            }
            
        }

        /*
        尝试使用%d接收字符a，使用scanf，看结果，是否出现直接跳过
        
        */
    }
    return 0;

}



//+++++++++++++++++++++




// #include <stdio.h>
// #include <stdlib.h>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <getopt.h>
// #include <netinet/in.h>
// #include <netinet/ip.h>
// //#include <proto.h>  
// #include <proto.h>
// #include <arpa/inet.h>
// #include <errno.h>
// #include <error.h>
// #include <string.h>
// #include <net/if.h>
// #include "client.h"

// /*
// -M --mgroup specify multicast group
// -P --port specify receive port
// -p --player specify player
// -H --help show help
// */

// struct client_cfg_st client_conf = {
//         .recvport = RECVPORT,
//         .mgroup = MULTIGROUP,
//         .player = PLAYER
//         };

// static void print_help()
// {
//     printf("-P --port   specify receive port\n");
//     printf("-M --mgroup specify multicast group\n");
//     printf("-p --player specify player \n");
//     printf("-H --help   show help\n");
// }
// /*write to fd len bytes data*/
// static int writen(int fd, const void * buf, size_t len)
// {
//     int count = 0;
//     int pos = 0;
//     while(len > 0)
//     {
//         count = write(fd, (char*)buf + pos, len);
//         if(count < 0)
//         {
//             if(errno == EINTR)
//                 continue;
//             perror("write()");
//             return -1;
//         }
//         len -= count;
//         pos += count;
//     }
//     return 0;

// }
// int main(int argc, char * argv[])
// {

//     int index = 0;
//     int sd = 0;
//     struct ip_mreqn mreq;//group setting
//     struct sockaddr_in laddr; //local address
//     int val;//set sockopt 
//     int pd[2];
//     pid_t pid;
//     struct sockaddr_in server_addr;
//     socklen_t serveraddr_len;
//     int len;
//     int chosenid;
//     int ret = 0;
//     struct msg_channel_st *msg_channel;
//     struct sockaddr_in raddr;
//     socklen_t raddr_len;
//     int c;


//     sd = socket(AF_INET, SOCK_DGRAM, 0);
//     if(sd < 0)
//     {
//         perror("socket()");
//         exit(0);
//     }
//     //multicast group 
//     inet_pton(AF_INET, client_conf.mgroup, &mreq.imr_multiaddr);//255.255.255.255-->0xFF..
//     //local address(self)
//     inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
//     //local net card
//     mreq.imr_ifindex = if_nametoindex("ens33");
//     if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
//     {
//         perror("setsockopt()");
//         exit(1);
//     }
//     val = 1;
//     //improve efficiency
//     if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &(val), sizeof(val)) < 0)
//     {
//         perror("setsockopt()");
//         exit(1);
//     }
//     laddr.sin_family = AF_INET;
//     laddr.sin_port = htons(atoi(client_conf.recvport));
//     inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);
//     if(bind(sd, (sockaddr*)&laddr, sizeof(laddr)) < 0)
//     {
//         perror("bind()");
//         exit(1);
//     }
//     if(pipe(pd) < 0 )
//     {
//         perror("pipe()");
//         exit(1);
//     }

//     pid = fork();
//     if(pid < 0)
//     {
//         perror("fork()"); 
//         exit(1);
//     }
//     if(pid == 0)//child, read, close write
//     {
//         /*decode*/
//         /*mpg123 read from stdin*/
//         // close(sd);//socket
//         // close(pd[1]);//0:read, 1:write
//         // dup2(pd[0], 0);//set pd[0] as stdin
//         // if(pd[0] > 0) //close pd[0]
//         //     close(pd[0]);
//         // /*use shell to parse DEFAULT_PLAYERCMD, NULL means to end*/
//         // execl("/bin/sh", "sh", "-c", client_conf.player, NULL);
//         // perror("execl()");
//         exit(1);
//     }
//     else//parent
//     {
//         /*receive data from network, write it to pipe*/
//         //receive programme
//         struct msg_list_st *msg_list;
//         msg_list = (msg_list_st*)malloc(MSG_LIST_MAX);
//         if(msg_list == NULL)
//         {
//             perror("malloc");
//             exit(1);
//         }
//         //必须从节目单开始
//         while(1)
//         {
//             len = recvfrom(sd, msg_list, MSG_LIST_MAX, 0, (sockaddr*)&server_addr, &serveraddr_len);
//             fprintf(stderr, "server_addr:%d\n", server_addr.sin_addr.s_addr);
//             if(len < sizeof(struct msg_list_st))
//             {
//                 fprintf(stderr, "massage is too short.\n");
//                 continue;
//             }
//             if(msg_list->channid != LISTCHANN)
//             {
//                 fprintf(stderr, "current chnid:%d.\n", msg_list->channid);
//                 fprintf(stderr, "chnid is not match.\n");
//                 continue;
//             }
//             break;
//         }

//         //printf programme, select channel
//         /*
//         1.music xxx
//         2.radio xxx
//         3.....
//         */
//         //receive channel package, send it to child process
//         // struct msg_listentry_st *pos;
//         // for(pos = msg_list->entity;(char*)pos < ((char *)msg_list + len);pos = (msg_list_entity_st*)((char *)pos) + ntohs(pos->len))
//         // {
//         //     printf("channel:%d%s", pos->chnid, pos->desc);
//         // }
//         // /*free list*/
//         // free(msg_list);
//         // while (ret < 1)
//         // {
//         //     ret = scanf("%d", &chosenid);
//         //     if(ret != 1)
//         //         exit(1);
//         // } 

//         // msg_channel = malloc(MSG_CHANNEL_MAX);
//         // if(msg_channel == NULL)
//         // {
//         //     perror("malloc");
//         //     exit(1);
//         // }
//         // raddr_len = sizeof(raddr);
//         // char ipstr_raddr[30];
//         // char ipstr_server_addr[30];
//         // while(1)
//         // {
//         //     len = recvfrom(sd, msg_channel, MSG_CHANNEL_MAX, 0, (void*)&raddr, &raddr_len);
//         //     fprintf(stderr, "raddr:%d\n", raddr.sin_addr.s_addr);
//         //     //防止有人恶意发送不相关的包
//         //     if(raddr.sin_addr.s_addr != server_addr.sin_addr.s_addr)
//         //     {
//         //         inet_ntop(AF_INET, &raddr.sin_addr.s_addr, ipstr_raddr, 30);
//         //         inet_ntop(AF_INET, &server_addr.sin_addr.s_addr, ipstr_server_addr, 30);
//         //         fprintf(stderr, "Ignore:addr not match. raddr:%s server_addr:%s.\n", ipstr_raddr, ipstr_server_addr);
//         //         continue;
//         //         //exit(1);
//         //     }
//         //     if(raddr.sin_port != server_addr.sin_port)
//         //     {
//         //         fprintf(stderr, "Ignore:port not match.\n");
//         //         continue;
//         //         //exit(1);  
//         //     }
//         //     if(len < sizeof(struct msg_channel_st))
//         //     {
//         //         fprintf(stderr, "Ignore:massage too short.\n");
//         //         continue;
//         //     }
//         //     //可以做一个缓冲机制，停顿1  2 秒，不采用接收一点播放一点
//         //     if(msg_channel->chnid == chosenid)
//         //     {
//         //         fprintf(stdout, "Accept massage:%d recived.\n", msg_channel->chnid);
//         //         if( writen(pd[1], msg_channel->data, len - sizeof(chnid_t)) < 0)/*write pipe*/
//         //             exit(1);
                    
//         //     }
//         // }

//         // free(msg_channel);
//         // close(sd);
//         exit(0);
//     }

// }